/***************************************************************************
                         menu.c  -  Part of gltris
                             -------------------
    begin                : April 2001
    copyright            : (C) 2001 by David Delbecq (Alias Tchize)
    email                : david.delbecq@usa.net
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

/****************************************************************************
* This module shows a menu according to some structures passed as argument  *
* a callback is received to notice meny exiting and another one to draw the *
* background.                                                               *
****************************************************************************/
#include "os_depend.h"
#include <GL/gl.h>
#include <GL/glut.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "defs.h"
#include "font.h"
#include "menu.h"

#define MENUSTATE_SELECTION 0
#define MENUSTATE_KEY_ACQUIRING 1
#define MENUSTATE_TEXT_ACQUIRING 2
int currentstate;
menuobj actualmenu=NULL;
callback_int finished=NULL;
callback_void predisplay=NULL;


char *key2text (int key)
        {
        static char buffer[256];
        char* keyfonctions[12]={"F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12"};
        char* keymove[9]={"LEFT","UP","RIGHT","DOWN","PG_UP","PG_DOWN","HOME","END","INSERT"};
        char touche;
        if (key&KEY_NORMAL_MASK)
                {
                touche=(char)(key ^ (KEY_NORMAL_MASK));
                switch (touche)
                        {
                        case ' ' :
                                snprintf (buffer,256,"< Space >");
                                break;
                        case 8 :
                                snprintf (buffer,256,"< Corr >");
                                break;
                        case 9 :
                                snprintf (buffer,256,"< Tab >");
                                break;
                        case 13 :
                                snprintf (buffer,256,"< Return >");
                                break;
                        case 27 :
                                snprintf (buffer,256,"< Esc >");
                                break;
                        case 127 :
                                snprintf (buffer,256,"< Del >");
                                break;
                        default:
                                snprintf (buffer,256,"%c",touche);
                                break;
                        }
                }
        else if (key&KEY_SPECIAL_MASK) switch (key ^ KEY_SPECIAL_MASK)
                {
                case GLUT_KEY_F1:
                case GLUT_KEY_F2:
                case GLUT_KEY_F3:
                case GLUT_KEY_F4:
                case GLUT_KEY_F5:
                case GLUT_KEY_F6:
                case GLUT_KEY_F7:
                case GLUT_KEY_F8:
                case GLUT_KEY_F9:
                case GLUT_KEY_F10:
                case GLUT_KEY_F11:
                case GLUT_KEY_F12:
                        snprintf (buffer,256,"< %s >",keyfonctions[(key ^KEY_SPECIAL_MASK)-GLUT_KEY_F1]);
                        break;
                case GLUT_KEY_LEFT:
                case GLUT_KEY_UP:
                case GLUT_KEY_RIGHT:
                case GLUT_KEY_DOWN:
                case GLUT_KEY_PAGE_UP:
                case GLUT_KEY_PAGE_DOWN:
                case GLUT_KEY_HOME:
                case GLUT_KEY_END:
                case GLUT_KEY_INSERT:
                        snprintf (buffer,256,"< %s >",keymove[(key ^KEY_SPECIAL_MASK)-GLUT_KEY_LEFT]);
                        break;
                default:
                        snprintf (buffer,256,"Unknown key: #%d",key ^KEY_SPECIAL_MASK);
                        break;
                }
        else snprintf (buffer,256,"??");
        return buffer;
        }
/***************************
* Goes one item up in menu *
***************************/
void menuup (void)
        {
        int oldactiveitem;
        oldactiveitem=actualmenu->activeitem;
        do
                actualmenu->activeitem=(actualmenu->activeitem+actualmenu->itemscount-1) % actualmenu->itemscount;
        while ( (!actualmenu->menuentries[actualmenu->activeitem].isusable) && (actualmenu->activeitem!=oldactiveitem));
        if (actualmenu->activeitem==oldactiveitem) printf ("Warning! There are no usable menuentries available. Didn't move\n");
        glutPostRedisplay();
        }

/*****************************
* Goes one item down in menu *
*****************************/
void menudown (void)
        {
        int oldactiveitem;
        oldactiveitem=actualmenu->activeitem;
        do
                actualmenu->activeitem=(actualmenu->activeitem+1) % actualmenu->itemscount;
        while ( (!actualmenu->menuentries[actualmenu->activeitem].isusable) && (actualmenu->activeitem!=oldactiveitem));
        if (actualmenu->activeitem==oldactiveitem) printf ("Warning! There are no usable menuentries available. Didn't move\n");
        glutPostRedisplay();
        }

void querycallback (int val)
        {
        glutKeyboardFunc (NULL);
        glutSpecialFunc (NULL);
        if (finished!=NULL) finished(val);
        return;
        }

/*******************************************************
* Exit from current menu and goes menu up if available *
*******************************************************/
void exitmenu(void)
        {
        if (actualmenu->previousmenu!=NULL)
                {
                actualmenu=actualmenu->previousmenu;
                currentstate=MENUSTATE_SELECTION;
                glutPostRedisplay ();
                return;
                }
        /* We definitly exit the menus..... */
        glutKeyboardFunc (NULL);
        glutSpecialFunc (NULL);
        if (finished!=NULL) finished(0);
        return;
        }

/**************************************
* press Enter on the current menuitem *
**************************************/
void applycurrentmenu (void)
        {
        switch (actualmenu->menuentries[actualmenu->activeitem].entrytype)
                {
                case MENU_TITLE:
                        break;
                case MENU_LINK:
                        setMenu ( (menuobj)actualmenu->menuentries[actualmenu->activeitem].data,NULL,predisplay); /*No callback*/
                        break;
                case MENU_EXIT:
                        exitmenu ();
                        break;
                case MENU_RETURNSTATUS:
                        querycallback ((int)actualmenu->menuentries[actualmenu->activeitem].data);
                        break;
                case MENU_KEY:
                        currentstate=MENUSTATE_KEY_ACQUIRING;
                        break;
                case MENU_TEXT:
                        currentstate=MENUSTATE_TEXT_ACQUIRING;
                        break;
                case MENU_BOOLEAN:
                        *((int*)actualmenu->menuentries[actualmenu->activeitem].data)=!(*(int*)actualmenu->menuentries[actualmenu->activeitem].data);
                        break;
                default:
                        break;
                }
        }


/*********************************************************
* get the pressed key and update infos in the data field *
*********************************************************/
void changecurrentkey (int key)
        {
        *(int*)actualmenu->menuentries[actualmenu->activeitem].data=key;
        currentstate=MENUSTATE_SELECTION;
        }

/****************************************************
* add the key character to the actual edited string *
****************************************************/
void addchar (unsigned char key)
        {
        char* text;
        char* text2;
        text=*(char**)actualmenu->menuentries[actualmenu->activeitem].data;
        if ((key==13) || (key==10) || (key==27))
                {
                currentstate=MENUSTATE_SELECTION;
                }
        if ( (key==8) && (text!=NULL) && (strlen(text)>0) )
                text[strlen(text)-1]='\0';
        if (!isLegalLetter (key)) return;
        if (text==NULL) text2=(char*)malloc (sizeof (char)*2);
        else text2=(char*)malloc (sizeof (char)*(strlen(text)+2));
        if (text2==NULL) return;
        if (text!=NULL) strcpy (text2,text);
        else text2[0]='\0';
        text2[strlen(text2)+1]='\0';
        text2[strlen(text2)]=key;
        *(char**)actualmenu->menuentries[actualmenu->activeitem].data=text2;
        if (text!=NULL) free (text);
        }

/***********************************
* Redisplay callback for the menus *
***********************************/
void menuRedisplay (void)
        {
        static int clignote=0;
        int width,height;
        int i;
        char buffer [1024];
        clignote++;
        if (clignote>120) clignote-=120;
        width=glutGet (GLUT_WINDOW_WIDTH);
        height=glutGet (GLUT_WINDOW_HEIGHT);
        glClearColor (0.0,0.0,0.0,1.0);
        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        if (predisplay!=NULL) predisplay();
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity ();
        glViewport (0,0,width,height);
        gluOrtho2D (0.0,width/20,-height/20,0.0);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity ();
        glTranslatef (5.0,-3.0,0.0);
        for (i=0;i<actualmenu->itemscount;i++)
                {
                switch (actualmenu->menuentries[i].entrytype)
                        {
                        case MENU_TITLE:
                        case MENU_RETURNSTATUS:
                        case MENU_LINK:
                        case MENU_EXIT:
                                snprintf (buffer,1023,"%s",actualmenu->menuentries[i].text);
                                break;
                        case MENU_TEXT:
                                if ( (i==actualmenu->activeitem) &&(currentstate!=MENUSTATE_SELECTION) && ((clignote/10)%3))
                                     snprintf (buffer,1023,"%s%s_",actualmenu->menuentries[i].text,*(char **)actualmenu->menuentries[i].data);
                                else snprintf (buffer,1023,"%s%s",actualmenu->menuentries[i].text,*(char **)actualmenu->menuentries[i].data);
                                break;
                        case MENU_KEY:
                                if ( (i==actualmenu->activeitem) &&(currentstate!=MENUSTATE_SELECTION))
                                     snprintf (buffer,1023,"%s%s",actualmenu->menuentries[i].text,"Press a key");
                                else snprintf (buffer,1023,"%s%s",actualmenu->menuentries[i].text,key2text (*(int *)actualmenu->menuentries[i].data));
                                break;
                        case MENU_BOOLEAN:
                              snprintf (buffer,1023,"%s %s",actualmenu->menuentries[i].text,(*(int *)actualmenu->menuentries[i].data)?"Yes":"No");
                              break;
                        default:
                                break;
                        }
                if (i==actualmenu->activeitem) glColor3f (1.0,0.0,0.0);
                else if (!actualmenu->menuentries[i].isusable) glColor3f (0.5,0.5,0.5);
                else glColor3f (1.0,1.0,1.0);
                DrawStringAt (buffer,0.0,-1.5*i);
                }
        glutSwapBuffers();
        }

/*********************************
* Special callback for the menus *
*********************************/
void menuSpecial (int key, int x, int y)
        {
        switch (currentstate)
                {
                case MENUSTATE_SELECTION:
                        switch (key)
                                {
                                case GLUT_KEY_UP:
                                        menuup();
                                        break;
                                case GLUT_KEY_DOWN:
                                        menudown ();
                                        break;
                                default:
                                        break;
                                }
                        break;
                case MENUSTATE_KEY_ACQUIRING:
                        changecurrentkey ( ((int)key)|KEY_SPECIAL_MASK);
                        break;
                default:
                        break;
                }

        }

/**********************************
* Keyboard callback for the menus *
**********************************/
void menuKeyboard (unsigned char key, int x, int y)
        {
        switch (currentstate)
                {
                case MENUSTATE_SELECTION:
                        if (key==13) applycurrentmenu();
                        if (key==27) exitmenu ();
                        break;
                case MENUSTATE_KEY_ACQUIRING:
                        changecurrentkey ( ((int)key)+KEY_NORMAL_MASK);
                        break;
                case MENUSTATE_TEXT_ACQUIRING:
                        addchar (key);
                        break;
                default:
                        break;
                }
        }

/**********************************
* Set the current menu being used *
**********************************/
void setMenu (menuobj menu,callback_int menufinished, callback_void display)
        {
        actualmenu=menu;
        if (menufinished!=NULL) finished=menufinished;
        predisplay=display;
        currentstate=MENUSTATE_SELECTION;
        glutDisplayFunc (menuRedisplay);
        glutKeyboardFunc (menuKeyboard);
        glutSpecialFunc (menuSpecial);
        glutPostRedisplay ();
        return;
        }
